import { Object3D } from "../../../entity/Object3D.js";
import { OBB } from "./Tiles/geometry/obb.js";
import { path } from "./Tiles/utils/path.js"
import { resolveImplicite } from './implicit/ImplicitTileResolver.js';
import { Sphere } from "../../../core/math/Sphere.js";
import { Vector2 } from "../../../core/math/Vector2.js";
import { Vector3 } from "../../../core/math/Vector3.js";
import { Quaternion } from "../../../core/math/Quaternion.js";
import { generateUUID } from "../../../core/math/MathUtils.js";
import { Matrix4 } from "../../../core/math/Matrix4.js";
import { Frustum } from "../../../core/math/Frustum.js";
import { Engine } from "../../../Engine.js";
import { Euler } from "../../../core/math/Euler.js";
import { proj } from "../../../core/crs/proj.js"
const tempSphere = new Sphere(new Vector3(0, 0, 0), 1);
const tempOBB = new OBB([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]);
const tempVec1 = new Vector3(0, 0, 0);
const tempVec2 = new Vector3(0, 0, 0);
const upVector = new Vector3(0, 0, 1);
const rendererSize = new Vector2(1000, 1000);
const tempQuaternion = new Quaternion();
class OGC3DTile {
    constructor(properties) {
        this.object3D = new Object3D()
        if (properties.url) {
            var results = properties.url.split('/');
            this.fileName = results[results.length - 2];
        }
        if (properties.parentTile) {
            this.id = properties.parentTile.id + properties.index
        } else {
            this.id = this.fileName + properties.index
        }
        this.object3D = new Object3D({ id: this.id })
        this.jsonID = properties.jsonID
        const self = this;
        this.contentURL = [];
        this.loadingStrategy = properties.loadingStrategy ? properties.loadingStrategy.toUpperCase() : "IMMEDIATE";//IMMEDIATE|INCREMENTAL
        this.distanceBias = Math.max(0.0001, properties.distanceBias ? properties.distanceBias : 1);
        this.uuid = generateUUID();
        // 本地矩阵
        this.localMat = new Matrix4()
        // 世界矩阵
        this.worldMat = new Matrix4()
        this.tileLoader = properties.tileLoader;
        this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
        this.meshCallback = properties.meshCallback;
        this.loadOutsideView = properties.loadOutsideView;
        this.cameraOnLoad = properties.cameraOnLoad;
        this.parentTile = properties.parentTile;
        // 声明特定于tile的属性
        this.childrenTiles = [];
        this.meshContent = [];
        this.tileContent;
        this.refine; // defaults to "REPLACE"
        this.rootPath;
        this.geometricError;
        this.boundingVolume;
        this.json; // 对应于这个tile的json
        this.materialVisibility = false;
        this.level = properties.level ? properties.level : 0;
        this.hasMeshContent = 0; // 当提供的json具有指向B3DM文件的内容字段时，为true
        this.hasUnloadedJSONContent = 0; // 当提供的json具有指向尚未加载的json文件的内容字段时，为true
        this.centerModel = properties.centerModel;
        this.abortController = new AbortController();
        if (!!properties.json) { // 如果此Tile是作为另一个Tile的子Tiles创建的，则properties。Json不是空的
            self._setup(properties);
        } else if (properties.url) { // tileset.jsonn的url   
            var url = properties.url;
            var fetchFunction;
            fetchFunction = () => {
                return fetch(url, { signal: self.abortController.signal });
            }
            fetchFunction().then(result => {
                if (!result.ok) {
                    throw new Error(`不能加载 "${properties.url}". 状态 ${result.status} : ${result.statusText}`);
                }
                result.json().then(json => { return resolveImplicite(json, url) }).then(json => {
                    self._setup({
                        rootPath: path.dirname(properties.url), json: json,
                    });
                });
            }).catch(e => { if (self.displayErrors) showError(e) });
        }
    }
    async _setup(properties) {
        const self = this;
        if (!!properties.json.root) {
            self.json = properties.json.root;
            if (!self.json.refine) self.json.refine = properties.json.refine;
            if (!self.json.geometricError) self.json.geometricError = properties.json.geometricError;
            if (!self.json.transform) self.json.transform = properties.json.transform;
            if (!self.json.boundingVolume) self.json.boundingVolume = properties.json.boundingVolume;
        } else {
            self.json = properties.json;
        }
        if (!self.json.children) {
            if (self.json.getChildren) {
                self.json.children = await self.json.getChildren();
            } else {
                self.json.children = [];
            }
        }
        self.rootPath = !!properties.json.rootPath ? properties.json.rootPath : properties.rootPath;
        // 解析 refine
        if (!!self.json.refine) {
            self.refine = self.json.refine;
        } else {
            self.refine = properties.parentRefine;
        }
        // 解析 geometric error
        if (!!self.json.geometricError) {
            self.geometricError = self.json.geometricError;
        } else {
            self.geometricError = properties.parentGeometricError;
        }
        // 解析 transform
        if (!!self.json.transform && !self.centerModel) {
            this.localMat.elements = self.json.transform
            if (this.tileLoader.crs === "4326") {
                let coord84 = proj("EPSG:4479", "EPSG:4326", [this.localMat.elements[12], this.localMat.elements[13], this.localMat.elements[14]])
                let scalexy = proj.scale([coord84[0], coord84[1]])
                let scale = [scalexy[0], scalexy[1], 110000]
                console.log(scale);
                // coord84[2]=coord84[2]/110000
                console.log(coord84)
                coord84[0] = coord84[0] + this.tileLoader.translation[0]
                coord84[1] = coord84[1] + this.tileLoader.translation[1]
                coord84[2] = this.tileLoader.translation[2]
                let out = new Matrix4()
                out.scale(new Vector3(1 / scale[0], 1 / scale[1], 1 / scale[2]))
                out.setPosition(coord84[0], coord84[1], coord84[2])
                this.localMat = out
                this.worldMat = this.worldMat.multiply(this.localMat)
                self.object3D.trans.applyMatrix4(this.localMat);
            } else {
                let coord84 = proj("EPSG:4479", "EPSG:3857", [this.localMat.elements[12], this.localMat.elements[13], this.localMat.elements[14]])
                coord84[0] = coord84[0] + this.tileLoader.translation[0]
                coord84[1] = coord84[1] + this.tileLoader.translation[1]
                coord84[2] = this.tileLoader.translation[2]
                let out = new Matrix4()
                out.scale(new Vector3(1.085, 1.09, 1))
                out.setPosition(coord84[0], coord84[1], coord84[2])
                this.localMat = out
                this.worldMat = this.worldMat.multiply(this.localMat)
                self.object3D.trans.applyMatrix4(this.localMat);
            }
        }
        if (this.parentTile) {
            const w = new Matrix4()
            this.worldMat = w.multiplyMatrices(this.parentTile.worldMat, this.worldMat)
        }
        // 解析 volume
        if (!!self.json.boundingVolume) {
            if (!!self.json.boundingVolume.box) {
                self.boundingVolume = new OBB(self.json.boundingVolume.box);
            } else if (!!self.json.boundingVolume.region) {
                const region = self.json.boundingVolume.region;
                self._transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
                self._transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
                tempVec1.lerp(tempVec2, 0.5);
                self.boundingVolume = new Sphere(new Vector3(tempVec1.x, tempVec1.y, tempVec1.z), tempVec1.distanceTo(tempVec2));
            } else if (!!self.json.boundingVolume.sphere) {
                const sphere = self.json.boundingVolume.sphere;
                self.boundingVolume = new Sphere(new Vector3(sphere[0], sphere[1], sphere[2]), sphere[3]);
            } else {
                self.boundingVolume = properties.parentBoundingVolume;
            }
        } else {
            self.boundingVolume = properties.parentBoundingVolume;
        }
        function _checkContent(e) {
            if (!!e.uri && e.uri.includes("json")) {
                self.hasUnloadedJSONContent++;
            } else if (!!e.url && e.url.includes("json")) {
                self.hasUnloadedJSONContent++;
            } else {
                self.hasMeshContent++;
            }
        }
        if (!!self.json.content) {
            _checkContent(self.json.content);
            if (self.hasMeshContent == 0) {
                self.level = Math.max(0, self.parentTile ? self.parentTile.level + 0.01 : 0.0);
            }
            switch (self.loadingStrategy) {
                case "IMMEDIATE": self._load(true, false); break;
                default: self._load();
            }
        } else if (!!self.json.contents) {
            self.json.contents.forEach(e => _checkContent(e))
            if (self.hasMeshContent == 0) {
                self.level = Math.max(0, self.parentTile ? self.parentTile.level + 0.01 : 0.0);
            }
            switch (self.loadingStrategy) {
                case "IMMEDIATE": self._load(true, false); break;
                default: self._load();
            }
        }
        if (!!self.centerModel) {
            tempVec2.copy(self.boundingVolume.center);
            if (!!this.json.boundingVolume.region) {
                this._transformWGS84ToCartesian(
                    (this.json.boundingVolume.region[0] + this.json.boundingVolume.region[2]) * 0.5,
                    (this.json.boundingVolume.region[1] + this.json.boundingVolume.region[3]) * 0.5,
                    (this.json.boundingVolume.region[4] + this.json.boundingVolume.region[5]) * 0.5,
                    tempVec1);
                tempQuaternion.setFromUnitVectors(tempVec1.normalize(), upVector.normalize());
                self.object3D.trans.applyQuaternion(tempQuaternion);
            }
            tempVec2.applyMatrix4(self.localMat);
            self.object3D.trans.position.sub(tempVec2);
        }
        if (properties.onLoadCallback) self.onLoadCallback(self);
        self.isSetup = true;
    }
    // 组装
    _assembleURL(root, relative) {
        // 如果根URL没有斜杠，在其后面添加斜杠
        if (!root.endsWith('/')) {
            root += '/';
        }
        const rootUrl = new URL(root);
        let rootParts = rootUrl.pathname.split('/').filter(p => p !== '');
        let relativeParts = relative.split('/').filter(p => p !== '');
        for (let i = 1; i <= rootParts.length; i++) {
            if (i >= relativeParts.length) break;
            const rootToken = rootParts.slice(rootParts.length - i, rootParts.length).join('/');
            const relativeToken = relativeParts.slice(0, i).join('/');
            if (rootToken === relativeToken) {
                for (let j = 0; j < i; j++) {
                    rootParts.pop();
                }
                break;
            }
        }
        while (relativeParts.length > 0 && relativeParts[0] === '..') {
            rootParts.pop();
            relativeParts.shift();
        }
        return `${rootUrl.protocol}//${rootUrl.host}/${[...rootParts, ...relativeParts].join('/')}`;
    }
    _extractQueryParams(url) {
        const urlObj = new URL(url);
        // 删除查询字符串
        urlObj.search = '';
        return urlObj.toString();
    }
    async _load(loadJson = true, loadMesh = true) {
        var self = this;
        if (self.deleted) return;
        if (!!self.json.content) {
            await loadContent(self.json.content, null, loadJson, loadMesh);
        } else if (!!self.json.contents) {
            let promises = self.json.contents.map((content, index) => loadContent(content, index, loadJson, loadMesh));
            Promise.all(promises)
        }
        async function loadContent(content, contentIndex, loadJson, loadMesh) {
            let url;
            if (!!content.uri) {
                url = content.uri;
            } else if (!!content.url) {
                url = content.url;
            }
            const urlRegex = /^(?:http|https|ftp|tcp|udp):\/\/\S+/;
            if (urlRegex.test(self.rootPath)) { // url
                if (!urlRegex.test(url)) {
                    url = self._assembleURL(self.rootPath, url);
                }
            } else { //path
                if (path.isAbsolute(self.rootPath)) {
                    url = self.rootPath + path.sep + url;
                }
            }
            url = self._extractQueryParams(url);
            if (!!url) {
                if (loadMesh && (url.includes(".b3dm") || url.includes(".glb") || url.includes(".gltf"))) {
                    self.contentURL.push(url);
                    try {
                        self.tileLoader.get(self.abortController, self.uuid, url, mesh => {
                            if (!!self.deleted) return;
                            self.meshContent.push(mesh);
                            mesh.setVisibility(false)
                            self.object3D.add(mesh);
                        }, !self.cameraOnLoad ? () => 0 : () => {
                            let multiplier = 1;
                            if ((self.metric && self.metric < 0) || self.deleted) multiplier = 2;
                            if (self.parentTile) {
                                return self.parentTile._calculateDistanceToCamera(self.cameraOnLoad) * multiplier * self.level;
                            } else {
                                return self._calculateDistanceToCamera(self.cameraOnLoad) * multiplier * self.level;
                            }
                        }, () => self._getSiblings(),
                            self.level,
                            !!self.json.boundingVolume.region ? false : true,
                        );
                    } catch (e) {
                        if (self.displayErrors) showError(e)
                    }
                } else if (loadJson && url.includes(".json")) {
                    self.jsonRequested = url;
                    self.tileLoader.get(self.abortController, self.uuid, url, async json => {
                        if (!!self.deleted) return;
                        json.rootPath = path.dirname(url);
                        self.json.children.push(json);
                        if (contentIndex == null) {
                            delete self.json.content;
                        } else {
                            delete self.json.contents.splice(contentIndex, 1);
                        }
                        self.hasUnloadedJSONContent--;
                    });
                }
            }
        }
    }
    traverse(callback) {
        callback(this);
        const children = this.childrenTiles;
        for (let i = 0, l = children.length; i < l; i++) {
            children[i].traverse(callback);
        }
    }
    dispose() {
        const self = this;
        self.meshContent = [];
        self.childrenTiles.forEach(tile => tile.dispose());
        self.deleted = true;
        if (!!self.contentURL) {
            self.contentURL.forEach(url => {
                self.tileLoader.invalidate(url, self.uuid);
            })
            self.contentURL = [];
        }
        if (!!self.abortController) { // 中断请求
            self.abortController.abort();
        }
    }
    removeXYRotation(complexMatrix) {
        const position = new Vector3();
        const scale = new Vector3();
        const quaternion = new Quaternion();
        complexMatrix.decompose(position, quaternion, scale)
        let euler = new Euler();
        euler.setFromQuaternion(quaternion, 'ZXY');
        // 从四元数中移除Z轴旋转（这里简化处理，实际上应使用更精确方法处理缩放和平移影响）
        const newQuaternion = new Quaternion().setFromEuler(new Euler(0, 0, 0));
        let out = new Matrix4()
        // const scale1 = new Vector3(1/110000,1/110000,1/110000);
        out.compose(position, newQuaternion, scale);
        return out
    }
    _disposeMeshContent() {
        const self = this;
        if (!!self.deleted) return;
        self.deleted = true;
        if (!!self.abortController) { // abort tile request
            self.abortController.abort("tile not needed");
            self.abortController = new AbortController();
        }
        for (let i = self.meshContent.length - 1; i >= 0; i--) {
            const mc = self.meshContent[i];
            const rootEntity = Engine.instance.scene.getEntity(mc.id)
            if (rootEntity) {
                Engine.instance.scene.removeEntity(rootEntity)
            }
        }
        self.meshContent = [];
        self.contentURL.forEach(url => {
            //console.log(url)
            self.tileLoader.invalidate(url, self.uuid);
        })
        self.contentURL = [];
    }
    _disposeChildren() {
        var self = this;
        self.childrenTiles.forEach(tile => {
            tile.dispose()
            const rootEntity = Engine.instance.scene.getEntity(tile.id)
            if (rootEntity) {
                Engine.instance.scene.removeEntity(rootEntity)
            }
        });
        self.childrenTiles = [];
        // if (!!self.meshContent) {
        //     self.meshContent.map(item => {
        //         const rootEntity = Engine.instance.scene.getEntity(item.id)
        //         if (!rootEntity) {
        //             Engine.instance.scene.addEntity(rootEntity)
        //         }
        //     })
        // }
    }
    update(camera) {
        const frustum = new Frustum();
        frustum.setFromProjectionMatrix(new Matrix4().multiplyMatrices(camera.project, camera.view));
        let numTiles = [0];
        let numTilesRendered = [0];
        let maxLOD = [0];
        let percentageLoaded = [0];
        if (this.refine == "REPLACE") {
            switch (this.loadingStrategy) {
                case "IMMEDIATE": this._updateImmediate(camera, frustum); this._statsImmediate(maxLOD, numTiles, percentageLoaded, numTilesRendered); break;
                default: this._update(camera, frustum); this._stats(maxLOD, numTiles, percentageLoaded, numTilesRendered);
            }
        } else {
            this._update(camera, frustum);
            this._stats(maxLOD, numTiles, percentageLoaded, numTilesRendered);
        }
        if (numTiles > 0) {
            percentageLoaded[0] /= numTiles[0];
        }
        return { numTilesLoaded: numTiles[0], numTilesRendered: numTilesRendered[0], maxLOD: maxLOD[0], percentageLoaded: percentageLoaded[0] }
    }
    _updateImmediate(camera, frustum) {
        this._computeMetricRecursive(camera, frustum);
        this._updateNodeVisibilityImmediate();
        this._expandTreeImmediate(camera);
        this.shouldBeVisible = this.metric > 0 ? true : !!this.loadOutsideView;
        this._shouldBeVisibleUpdateImmediate();
        this._trimTreeImmediate();
        this._loadMeshImmediate();
    }
    _statsImmediate(maxLOD, numTiles, percentageLoaded, numTilesRendered) {
        maxLOD[0] = Math.max(maxLOD[0], this.level);
        if (this.shouldBeVisible || !!this.materialVisibility) {
            numTiles[0]++;
            if (!!this.materialVisibility) percentageLoaded[0]++;
        }
        if (!!this.materialVisibility) numTilesRendered[0]++;
        this.childrenTiles.forEach(child => {
            child._statsImmediate(maxLOD, numTiles, percentageLoaded, numTilesRendered);
        })
    }
    _stats(maxLOD, numTiles, percentageLoaded, numTilesRendered) {
        maxLOD[0] = Math.max(maxLOD[0], this.level);
        if (this.hasMeshContent) {
            numTiles[0]++;
            if (this.meshContent.length == this.hasMeshContent) percentageLoaded[0]++;
            if (!!this.materialVisibility) numTilesRendered[0]++;
        }
        this.childrenTiles.forEach(child => {
            child._stats(maxLOD, numTiles, percentageLoaded, numTilesRendered);
        })
    }
    _trimTreeImmediate() {
        if (this.metric == undefined) return;
        else if (this.hasMeshContent && ((this.shouldBeVisible && this.materialVisibility))) {
            this._disposeChildren();
        } else {
            this.childrenTiles.forEach(child => {
                child._trimTreeImmediate();
            })
        }
    }
    /**
  * 立即更新节点及其子节点的可见性
  * 处理复杂的可见性逻辑，包括LOD切换和资源管理
  * @param {boolean} parentDisplaysMesh - 父节点是否显示网格
  */
    _updateNodeVisibilityImmediate(parentDisplaysMesh = false) {
        const self = this;
        // 如果没有网格内容，直接递归处理子节点
        if (!self.hasMeshContent) {
            self.childrenTiles.forEach(child => {
                child._updateNodeVisibilityImmediate(parentDisplaysMesh);
            })
            return;
        }
        // 处理应该可见的节点
        if (self.shouldBeVisible) {
            // 如果网格内容已完全加载
            if (self.meshContent.length == self.hasMeshContent) {
                // 如果当前不可见，设为可见并更新子节点
                if (!self.materialVisibility) {
                    self._changeContentVisibility(true);
                    self.childrenTiles.forEach(child => {
                        child._updateNodeVisibilityImmediate(parentDisplaysMesh);
                    })
                } else {
                    // 如果当前可见，递归更新子节点
                    self.childrenTiles.forEach(child => {
                        child._updateNodeVisibilityImmediate(true);
                    })
                }
            } else {
                // 网格未完全加载，继续处理子节点
                self.childrenTiles.forEach(child => {
                    child._updateNodeVisibilityImmediate(parentDisplaysMesh);
                })
            }
        }
        // 处理不应该可见的节点
        else {
            // 如果在视锥体外且不需要加载视锥体外的内容
            if (!self.loadOutsideView && self.metric < 0) {
                self._changeContentVisibility(false);
                if (self.meshContent.length > 0) self._disposeMeshContent();
                self.childrenTiles.forEach(child => {
                    child._updateNodeVisibilityImmediate(true);
                })
                return;
            }
            // 处理当前可见的节点
            if (self.materialVisibility) {
                // 如果父节点显示网格，隐藏当前节点
                if (parentDisplaysMesh) {
                    self._changeContentVisibility(false);
                    if (self.meshContent.length > 0) self._disposeMeshContent();
                    self.childrenTiles.forEach(child => {
                        child._updateNodeVisibilityImmediate(parentDisplaysMesh);
                    })
                } else {
                    // 检查所有子节点是否准备就绪
                    let allChildrenReady = true;
                    self.childrenTiles.every(child => {
                        if (!child._isReadyImmediate()) {
                            allChildrenReady = false;
                            return false;
                        }
                        return true;
                    });
                    // 如果所有子节点就绪，隐藏当前节点
                    if (allChildrenReady && self.childrenTiles.length > 0) {
                        self._changeContentVisibility(false);
                        if (self.meshContent.length > 0) self._disposeMeshContent();
                        self.childrenTiles.forEach(child => {
                            child._updateNodeVisibilityImmediate(parentDisplaysMesh);
                        })
                    } else {
                        // 否则继续显示当前节点
                        self.childrenTiles.forEach(child => {
                            child._updateNodeVisibilityImmediate(false);
                        })
                    }
                }
            } else {
                // 当前节点不可见，继续处理子节点
                self.childrenTiles.forEach(child => {
                    child._updateNodeVisibilityImmediate(parentDisplaysMesh);
                })
            }
        }
    }
    /**
  * 立即更新tile及其子节点的可见性状态
  * 根据不同条件决定每个tile是否应该可见
  */
    _shouldBeVisibleUpdateImmediate() {
        const self = this;
        // 情况1: 如果没有网格内容
        if (!self.hasMeshContent) {
            // 将所有子节点设为可见并递归更新
            self.childrenTiles.forEach(child => {
                child.shouldBeVisible = true;
                child._shouldBeVisibleUpdateImmediate();
            })
            // 当前节点设为不可见（因为没有内容）
            self.shouldBeVisible = false;
        }
        // 情况2: 如果metric未定义（尚未计算）
        else if (self.metric == undefined) {
            self.shouldBeVisible = false;
        }
        // 情况3: 如果在视锥体外（metric < 0）
        else if (self.metric < 0) {
            // 根据loadOutsideView设置决定是否可见
            self.shouldBeVisible = !!self.loadOutsideView;
            // 递归设置所有子节点为不可见
            self.childrenTiles.forEach(child => {
                child._setShouldNotBeVisibleRecursive();
            });
        }
        // 情况4: 如果几何误差小于阈值（需要显示更详细的层级）
        else if (self.metric < self.geometricErrorMultiplier * self.geometricError) {
            // 如果还有未加载的JSON内容，暂不处理
            if (self.hasUnloadedJSONContent) {
                //self.shouldBeVisible = true;
            } else {
                // 如果有子节点
                if (!!self.json && !!self.json.children && self.json.children.length > 0) {
                    // 当前节点设为不可见
                    self.shouldBeVisible = false;
                    // 子节点设为可见并递归更新
                    self.childrenTiles.forEach(child => {
                        child.shouldBeVisible = true;
                        child._shouldBeVisibleUpdateImmediate();
                    })
                } else {
                    // 如果没有子节点，当前节点设为可见
                    self.shouldBeVisible = true;
                }
            }
        }
        // 情况5: 几何误差大于阈值
        else {
            // 递归设置所有子节点为不可见
            self.childrenTiles.forEach(child => {
                child._setShouldNotBeVisibleRecursive();
            });
        }
    }
    _setShouldNotBeVisibleRecursive() {
        const self = this;
        self.shouldBeVisible = false;
        self.childrenTiles.forEach(child => {
            child._setShouldNotBeVisibleRecursive();
        })
    }
    _loadMeshImmediate() {
        const self = this;
        if (!self.hasMeshContent) {
            self.childrenTiles.forEach(child => {
                child._loadMeshImmediate();
            });
            return;
        }
        if (self.shouldBeVisible) {
            if (self.meshContent.length < self.hasMeshContent &&
                self.contentURL.length == 0) {
                self.deleted = false;
                self._load(false, true);
            }
        } else {
            self.childrenTiles.forEach(child => {
                child._loadMeshImmediate();
            })
        }
    }
    _computeMetricRecursive(camera, frustum) {
        const self = this;
        self.metric = -1;
        if (!self.isSetup) return;
        if (!!self.boundingVolume && !!self.geometricError) {
            self.metric = self._calculateUpdateMetric(camera, frustum);
        }
        self.childrenTiles.forEach(child => child._computeMetricRecursive(camera, frustum));
    }
    _expandTreeImmediate(camera) {
        const self = this;
        if (!self.hasUnloadedJSONContent) {
            if (!self.hasMeshContent) {
                // 如果有未加载的子节点，则加载它们
                if (!!self.json && !!self.json.children && self.childrenTiles.length < self.json.children.length) {
                    self._loadJsonChildren(camera);
                }
            } else {
                if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
                } else {
                    if (self.metric >= 0 && self.metric < self.geometricErrorMultiplier * self.geometricError &&
                        !!self.json && !!self.json.children && self.childrenTiles.length < self.json.children.length
                    ) {
                        self._loadJsonChildren(camera);
                    }
                }
            }
        }
        self.childrenTiles.forEach(child => child._expandTreeImmediate(camera));
    }
    _update(camera, frustum) {
        const self = this;
        if (!self.isSetup) return;
        const visibilityBeforeUpdate = self.materialVisibility;
        if (!!self.boundingVolume && !!self.geometricError) {
            self.metric = self._calculateUpdateMetric(camera, frustum);
        }
        self.childrenTiles.forEach(child => child._update(camera, frustum));
        _updateNodeVisibility(self.metric);
        _updateTree(self.metric);
        _trimTree(self.metric, visibilityBeforeUpdate);
        function _updateTree(metric) {
            if (metric < 0 && self.hasMeshContent) return;
            if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
                return;
            }
            if (!self.hasMeshContent || (metric <= self.geometricErrorMultiplier * self.geometricError && self.meshContent.length > 0)) {
                if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
                    self._loadJsonChildren(camera);
                    return;
                }
            }
        }
        function _updateNodeVisibility(metric) {
            if (!self.hasMeshContent) return;
            if (self.meshContent.length < self.hasMeshContent) {
                return;
            }
            if (metric < 0) {
                self.inFrustum = false;
                self._changeContentVisibility(!!self.loadOutsideView);
                return;
            } else {
                self.inFrustum = true;
            }
            if (self.childrenTiles.length == 0) {
                self._changeContentVisibility(true);
                return;
            }
            if (metric >= self.geometricErrorMultiplier * self.geometricError) {
                self._changeContentVisibility(true);
            } else if (metric < self.geometricErrorMultiplier * self.geometricError) {
                if (self.refine == "REPLACE") {
                    let allChildrenReady = true;
                    self.childrenTiles.every(child => {
                        if (!child._isReady()) {
                            allChildrenReady = false;
                            return false;
                        }
                        return true;
                    });
                    if (allChildrenReady) {
                        self._changeContentVisibility(false);
                    }
                    else {
                        self._changeContentVisibility(true);
                    }
                }
            }
        }
        function _trimTree(metric, visibilityBeforeUpdate) {
            if (!self.hasMeshContent) return;
            if (!self.inFrustum) { // 视锥体外
                self._disposeChildren();
                // updateNodeVisibility(metric);
                return;
            }
            if (self.occlusionCullingService &&
                !visibilityBeforeUpdate &&
                self.hasMeshContent &&
                self.meshContent.length > 0 &&
                self.materialVisibility &&
                self._areAllChildrenLoadedAndHidden()) {
                if (self.splatsMesh && this.materialVisibility && !self.splatsReady) {
                    return;
                }
                self._disposeChildren();
                //_updateNodeVisibility(metric);
                return;
            }
            if (metric >= self.geometricErrorMultiplier * self.geometricError) {
                self._disposeChildren();
                //_updateNodeVisibility(metric);
                return;
            }
        }
    }
    _loadJsonChildren(camera) {
        const self = this;
        for (let i = self.json.children.length - 1; i >= 0; i--) {
            if (!self.json.children[i].root && !self.json.children[i].children && !self.json.children[i].getChildren && !self.json.children[i].content && !self.json.children[i].contents) {
                self.json.children.splice(i, 1);
            }
        }
        self.json.children.forEach((childJSON, index) => {
            let childTile = new OGC3DTile({
                parentTile: self,
                parentGeometricError: self.geometricError,
                parentBoundingVolume: self.boundingVolume,
                parentRefine: self.refine,
                json: childJSON,
                rootPath: self.rootPath,
                geometricErrorMultiplier: self.geometricErrorMultiplier,
                loadOutsideView: self.loadOutsideView,
                level: Math.floor(self.level) + 1,
                tileLoader: self.tileLoader,
                cameraOnLoad: camera,
                centerModel: false,
                displayErrors: self.displayErrors,
                distanceBias: self.distanceBias,
                loadingStrategy: self.loadingStrategy,
                drawBoundingVolume: self.drawBoundingVolume,
                index: index,
            });
            self.childrenTiles.push(childTile);
            self.object3D.add(childTile.object3D);
        });
    }
    /**
  * 检查所有子节点是否都已加载且处于隐藏状态
  * 用于确定是否可以优化或卸载子节点
  * @returns {boolean} 如果所有子节点都已加载且隐藏返回true，否则返回false
  */
    _areAllChildrenLoadedAndHidden() {
        let allLoadedAndHidden = true;
        const self = this;
        // 遍历所有子节点进行检查
        this.childrenTiles.every(child => {
            // 如果子节点有网格内容
            if (child.hasMeshContent) {
                // 如果子节点还有自己的子节点，说明层级未到底，返回false
                if (child.childrenTiles.length > 0) {
                    allLoadedAndHidden = false;
                    return false;
                }
                // 如果子节点在视锥体内，继续检查
                if (!child.metric < 0) {
                    return true;
                };
                // 如果子节点可见且点云已就绪（或无点云），返回false
                if (child.materialVisibility && (!self.splatsMesh || self.splatsReady)) {
                    allLoadedAndHidden = false;
                    return false;
                }
                // 如果子节点在遮挡剔除服务中可见，返回false
                else if (self.occlusionCullingService.hasID(child.colorID)) {
                    allLoadedAndHidden = false;
                    return false;
                }
            }
            // 如果子节点没有网格内容，递归检查其子节点
            else {
                if (!child._areAllChildrenLoadedAndHidden()) {
                    allLoadedAndHidden = false;
                    return false;
                }
            }
            return true; // 继续检查下一个子节点
        });
        return allLoadedAndHidden;
    }
    /**
     * 检查tile是否准备就绪
     * 通过检查多个条件来确定tile是否可以被渲染或进一步处理
     * @returns {boolean} 如果tile准备就绪返回true，否则返回false
     */
    _isReady() {
        // 检查是否在视锥体外（metric未定义说明尚未计算）
        if (this.metric == undefined) {
            return false;
        }
        // 如果在视锥体外（metric < 0），认为已就绪
        if (this.metric < 0) {
            return true;
        }
        // 检查JSON内容是否还在加载中
        if (this.hasUnloadedJSONContent) {
            return false;
        }
        // 检查是否为空tile（没有网格内容、没有子节点、没有待加载的JSON）
        if (!this.hasMeshContent && this.json.children.length == 0 && !this.hasUnloadedJSONContent) {
            return true;
        }
        // 如果tile没有网格内容、网格未加载完成、不可见或点云网格未就绪，则检查子节点
        if ((!this.hasMeshContent || this.meshContent.length == 0 ||
            (!this.materialVisibility) || (self.splatsMesh && !self.splatsReady))) {
            // 如果有子节点，检查所有子节点是否就绪
            if (this.childrenTiles.children.length > 0) {
                var allChildrenReady = true;
                this.childrenTiles.every(child => {
                    if (!child._isReady()) {
                        allChildrenReady = false;
                        return false; // 中断循环
                    }
                    return true; // 继续循环
                });
                return allChildrenReady;
            } else {
                return false; // 没有子节点且自身未就绪
            }
        }
        // 如果没有网格内容（可能是中间节点），认为已就绪
        if (!this.hasMeshContent) {
            return true;
        }
        // 检查网格内容是否完全加载
        if (this.meshContent.length < this.hasMeshContent) {
            return false;
        }
        // 检查材质可见性和点云网格状态
        if (this.materialVisibility && (!this.splatsMesh || this.splatsReady)) {
            return true;
        }
        return false;
    }
    /**
     * 检查tile是否准备就绪（用于即时加载策略）
     * @returns {boolean} 如果tile及其所需的所有资源都已准备就绪则返回true
     */
    _isReadyImmediate() {
        // 如果tile当前可见，或者在视锥体外且不需要加载视锥体外的内容，则认为已就绪
        if (!!this.materialVisibility || (!this.loadOutsideView && this.metric < 0)) {
            return true;
        } else {
            // 检查子节点状态
            if (this.childrenTiles.length > 0) {
                // 检查所有子节点是否都已就绪
                var allChildrenReady = true;
                this.childrenTiles.every(child => {
                    if (!child._isReadyImmediate()) {
                        allChildrenReady = false;
                        return false; // 中断循环
                    }
                    return true; // 继续循环
                });
                return allChildrenReady;
            } else {
                // 如果没有子节点，检查点云网格状态
                if (this.splatsMesh) {
                    // 如果点云网格已就绪则返回true
                    if (this.splatsReady) {
                        return true
                    } else {
                        return false;
                    }
                } else {
                    // 既没有子节点也没有点云网格，表示未就绪
                    return false;
                }
            }
        }
    }
    /**
     * 改变tile内容的可见性
     * @param {boolean} visibility - 是否可见
     */
    _changeContentVisibility(visibility) {
        const self = this;
        // 如果tile有mesh内容且已加载
        if (self.hasMeshContent && self.meshContent.length > 0) {
            if (visibility) {
                // 显示所有mesh内容
                self.meshContent.forEach(mc => {
                    mc.setVisibility(true)
                })
            } else {
                // 隐藏所有mesh内容
                self.meshContent.forEach(mc => {
                    mc.setVisibility(false)
                })
            }
        }
        // 如果当前可见性状态与目标状态相同，直接返回
        if (self.materialVisibility == visibility) {
            return;
        }
        // 更新材质可见性状态
        self.materialVisibility = visibility
    }
    /**
 * 计算tile的更新度量值，用于决定LOD级别
 * @param {Camera} camera - 相机对象
 * @param {Frustum} frustum - 视锥体对象
 * @returns {number} 更新度量值。-1表示在视锥体外，0表示距离为0，其他正值表示需要更新的程度
 */
    _calculateUpdateMetric(camera, frustum) {
        let distance = 0;
        // 根据包围体类型计算到相机的距离
        if (this.boundingVolume instanceof OBB) {
            // 处理方向包围盒(OBB)
            tempOBB.copy(this.boundingVolume);
            // 应用世界变换矩阵
            tempOBB.applyMatrix4(this.worldMat);
            // 如果不在视锥体内，返回-1
            if (!tempOBB.inFrustum(frustum)) return -1;
            // 计算包围盒到相机的距离，减去近平面距离
            distance = Math.max(0, tempOBB.distanceToPoint(camera.trans.position) - camera.near);
        } else if (this.boundingVolume instanceof Sphere) {
            // 处理包围球
            tempSphere.copy(this.boundingVolume);
            // 应用世界变换矩阵
            tempSphere.applyMatrix4(this.worldMat);
            // 如果不在视锥体内，返回-1
            if (!frustum.intersectsSphere(tempSphere)) return -1;
            // 计算球心到相机的距离，减去球半径和近平面距离
            distance = Math.max(0, camera.trans.position.distanceTo(tempSphere.center) - tempSphere.radius - camera.near);
        } else {
            console.error("暂不支持其他形状");
            return -1
        }
        // 应用距离偏差，用于调整LOD切换的灵敏度
        distance = Math.pow(distance, this.distanceBias);
        if (distance == 0) {
            return 0;
        }
        // 获取世界变换的最大缩放因子
        const scale = this.worldMat.getMaxScaleOnAxis();
        // 获取渲染器尺寸
        rendererSize.x = Engine.instance.size[0]
        rendererSize.y = Engine.instance.size[1]
        // 根据相机纵横比选择合适的尺寸和视场角
        let s = rendererSize.y;
        let fov = camera.fovy;
        if (camera.aspect < 1) {
            // 如果是竖屏，调整视场角和使用宽度
            fov *= camera.aspect;
            s = rendererSize.x;
        }
        // 计算屏幕空间误差
        // lambda是在给定距离下视锥体的宽度
        let lambda = 2.0 * Math.tan(0.5 * fov) * distance;
        // 返回最终的度量值，考虑设备像素比、屏幕尺寸和缩放因子
        // 值越大表示需要更高的LOD级别
        return (window.devicePixelRatio * 16 * lambda) / (s * scale);
    }
    /**
    * 获取当前tile的兄弟节点
    * 兄弟节点定义为:与当前tile共享同一个具有mesh内容的最近父节点的其他子节点
    * @returns {Array} 返回兄弟tile数组
    */
    _getSiblings() {
        const self = this;
        const tiles = [];
        // 如果没有父节点，返回空数组
        if (!self.parentTile) return tiles;
        // 向上查找最近的具有mesh内容的父节点
        let p = self.parentTile;
        while (!p.hasMeshContent && !!p.parentTile) {
            p = p.parentTile;
        }
        // 遍历找到的父节点的所有子节点
        p.childrenTiles.forEach(child => {
            // 排除自身且确保child存在
            if (!!child && child != self) {
                // 对于每个子节点，向下查找直到找到具有mesh内容的节点
                while (!child.hasMeshContent && !!child.childrenTiles[0]) {
                    child = child.childrenTiles[0];
                }
                // 将找到的节点添加到兄弟节点数组中
                tiles.push(child);
            }
        });
        return tiles;
    }
    _calculateDistanceToCamera(camera) {
        if (this.boundingVolume instanceof OBB) {
            // box
            tempOBB.copy(this.boundingVolume);
            // tempSphere.applyMatrix4(this.object3d.trans.matrixWorld);
            tempOBB.applyMatrix4(this.worldMat);
            return Math.max(0, tempOBB.distanceToPoint(camera.trans.position));
            //if (!frustum.intersectsSphere(tempSphere)) return -1;
        } else if (this.boundingVolume instanceof Sphere) {
            //sphere
            tempSphere.copy(this.boundingVolume);
            // tempSphere.applyMatrix4(this.object3d.trans.matrixWorld);
            tempSphere.applyMatrix4(this.worldMat);
            return Math.max(0, camera.trans.position.distanceTo(tempSphere.center) - tempSphere.radius);
            //if (!frustum.intersectsSphere(tempSphere)) return -1;
        }
        else {
            console.error("unsupported shape");
            return -1;
        }
    }
    setGeometricErrorMultiplier(geometricErrorMultiplier) {
        this.geometricErrorMultiplier = geometricErrorMultiplier;
        this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
    }
    setDistanceBias(distanceBias) {
        this.distanceBias = distanceBias;
        this.childrenTiles.forEach(child => child.setDistanceBias(distanceBias));
    }
    _transformWGS84ToCartesian(lon, lat, h, sfct) {
        const a = 6378137.0;
        const e = 0.006694384442042;
        const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
        const cosLat = Math.cos(lat);
        const cosLon = Math.cos(lon);
        const sinLat = Math.sin(lat);
        const sinLon = Math.sin(lon);
        const nPh = (N + h);
        const x = nPh * cosLat * cosLon;
        const y = nPh * cosLat * sinLon;
        const z = (0.993305615557957 * N + h) * sinLat;
        sfct.set(x, y, z);
    }
}
export { OGC3DTile };
function showError(error) {
    var errorDiv = document.createElement("div");
    errorDiv.textContent = error;
    errorDiv.style.position = 'fixed';
    errorDiv.style.top = '10px';
    errorDiv.style.left = '50%';
    errorDiv.style.transform = 'translateX(-50%)';
    errorDiv.style.padding = '10px';
    errorDiv.style.backgroundColor = '#ff8800';
    errorDiv.style.color = '#ffffff';
    errorDiv.style.zIndex = '9999';
    document.body.appendChild(errorDiv);
    setTimeout(function () {
        errorDiv.remove();
    }, 8000);
}
